#!/usr/bin/perl

use strict;
use warnings;
use IO::File;
use Getopt::Long;
use File::Basename;
use lib dirname (__FILE__);
use Firmware::Silead;

sub usage() {
	print STDERR "Usage: fwtool {-i | -c <original.fw> | -s | -x <legacy.fw>} [ -1 | -2 | -3 ] [-w <width>] [-h <height>] [-t <num_touches>] [-f <flags>] <firmware.fw>\n";
	print STDERR "-i    Prints statistics and information about new-style firmware.fw\n";
	print STDERR "-c    Converts any older firmware format into new-style firmware.fw\n";
	print STDERR "-s    Sets parameters and flags\n";
	print STDERR "-x    Exports a firmware into plain format, to use with other Linux/Android drivers\n";
	print STDERR "      The source firmware format can be specified with these parameters:\n";
	print STDERR "-1    Plain firmware, as used with the Android driver (default)\n";
	print STDERR "-2    Windows GSL_TS_CFG.h firmware\n";
	print STDERR "-3    Scrambled Windows SileadTouch.fw firmware\n";
	print STDERR "      For the -c and -s modes, the following parameters may be set:\n";
	print STDERR "-m    Sets the controller model (4-byte ASCII string, ex.: 1680)\n";
	print STDERR "-w    Sets the width parameter\n";
	print STDERR "-h    Sets the height parameter\n";
	print STDERR "-t    Sets the number of supported touch points parameter\n";
	print STDERR "-f    Sets flags, separated by commas (optional, supported flags: xflip, yflip, swap, track)\n";
	print STDERR "      xflip enables horizontal flipping\n";
	print STDERR "      yflip enables vertical flipping\n";
	print STDERR "      swap enables axis swapping\n";
	print STDERR "      track enables in-driver finger tracking (use for controllers that don't support it)\n";
	print STDERR "      Each flag may be prefixed with 'no' to disable it.\n";
	-1;
}

print STDERR "-- fwtool --\n";
print STDERR "Firmware conversion and manipulation tool for Silead touchscreen controllers\n";
print STDERR "Copyright (c) 2016-2017 Gregor Riepl <onitake\@gmail.com>\n";
print STDERR "\n";
print STDERR "WARNING --- This tool is not endorsed or supported by Silead Inc.\n";
print STDERR "Use at your own risk.\n";
print STDERR "Not suitable for use with any other drivers except gslx680-acpi.\n";
print STDERR "\n";

my ($mode, $format, $tscfg, $model, $width, $height, $touches, $flags, $plain) = ('info', 'plain');
GetOptions(
	'info' => sub { $mode = 'info'; },
	'convert=s' => sub { $mode = 'convert'; $tscfg = $_[1]; },
	'set' => sub { $mode = 'set'; },
	'xport=s' => sub { $mode = 'export'; $plain = $_[1]; },
	'model=s' => \$model,
	'width=i' => \$width,
	'height=i' => \$height,
	'touches=i' => \$touches,
	'flags=s' => \$flags,
	'1' => sub { $format = 'plain' },
	'2' => sub { $format = 'tscfg' },
	'3' => sub { $format = 'scrambled' },
) or exit usage;

my $fwfile = $ARGV[0] or exit usage;

sub set_params {
	my ($rep) = @_;
	if (defined $model) {
		$rep->set_model($model);
	}
	if (defined $width) {
		$rep->set_width($width);
	}
	if (defined $height) {
		$rep->set_height($height);
	}
	if (defined $touches) {
		$rep->set_touches($touches);
	}
	if (defined $flags) {
		for my $flag (split /,\s*/, $flags) {
			if ($flag =~ /^(no)?xflip$/) {
				$rep->set_xmirrored($flag !~ /^no/);
			} elsif ($flag =~ /^(no)?yflip$/) {
				$rep->set_ymirrored($flag !~ /^no/);
			} elsif ($flag =~ /^(no)?swap$/) {
				$rep->set_swapped($flag !~ /^no/);
			} elsif ($flag =~ /^(no)?track$/) {
				$rep->set_tracking($flag !~ /^no/);
			} else {
				warn "Invalid flag: $flag";
			}
		}
	}
}

if ($mode eq 'info') {
	print "Loading $fwfile...\n";
	my $rep = Firmware::Silead->load($fwfile) or die "Can't load firmware $fwfile: $@";
	print "Controller model: " . $rep->model . "\n";
	print "Panel width: " . $rep->width . "\n";
	print "Panel height: " . $rep->height . "\n";
	print "Number of touch points: " . $rep->touches . "\n";
	print "X axis flipped: " . ($rep->xmirrored ? "yes" : "no") . "\n";
	print "Y axis flipped: " . ($rep->ymirrored ? "yes" : "no") . "\n";
	print "X and Y axes swapped: " . ($rep->swapped ? "yes" : "no") . "\n";
	print "Software tracking enabled: " . ($rep->tracking ? "yes" : "no") . "\n";
	my @pages = $rep->get_pages;
	print "Number of pages: ". scalar(@pages) . "\n";
	print "Page list:";
	for my $page (@pages) {
		printf " %02x", $page;
	}
	print "\n";
} elsif ($mode eq 'set') {
	print "Loading $fwfile...\n";
	my $rep = Firmware::Silead->load($fwfile) or die "Can't load firmware $fwfile: $@";
	print "Setting parameters...\n";
	set_params($rep);
	print "Saving $fwfile...\n";
	$rep->save($fwfile);
} elsif ($mode eq 'convert') {
	print "Loading $tscfg...\n";
	my $data;
	do {
		my $in = IO::File->new($tscfg, 'r') or die "Can't open $tscfg: $!";
		$in->binmode;
		local $/ = undef;
		$data = <$in>;
		defined($data) or die "Can't load firmware: $!";
		$in->close();
	};
	
	my $rep = Firmware::Silead->new();
	if ($format eq 'plain') {
		$rep->import_fw($data) or die "Can't parse firmware: $@";
	} elsif ($format eq 'tscfg') {
		$rep->import_tscfg($data) or die "Can't parse firmware: $@";
	} elsif ($format eq 'scrambled') {
		$rep->import_scrambled($data) or die "Can't parse firmware: $@";
	}
	
	print "Setting parameters...\n";
	set_params($rep);

	print "Saving $fwfile...\n";
	$rep->save($fwfile) or die "Can't write firmware: $@";
} elsif ($mode eq 'export') {
	print "Loading $fwfile...\n";
	my $rep = Firmware::Silead->load($fwfile) or die "Can't load firmware $fwfile: $@";
	print "Exporting to $plain...\n";
	my $out = IO::File->new($plain, 'w') or die "Can't open $plain: $!";
	my $data = $rep->export_fw();
	$out->print($data) or die "Can't write firmware: $!";
	$out->close();
}
